import os.path
import xmindparser
import pandas as pd
import tkinter as tk
from tkinter import filedialog
from openpyxl import load_workbook
from openpyxl.worksheet.datavalidation import DataValidation

"""用于xmind文件转成excel文件，xmind文件有格式要求
xmind格式：根节点---分组节点1--分组节点2---用例名称---前置条件---步骤描述---预期结果
                  节点1-----节点2-------节点3------节点4-----节点5----节点6
xmind格式：需求版本-分组一---分组二---用例名称-前置条件-步骤描述-预期结果（导出的excel表头）
版本：v1.0；优化优先级回填，修复分组列的bug；2024-10-26
"""


def parse_xmind_to_rows(xmind_file, file_name):
    # 解析 XMind 文件
    content = xmindparser.xmind_to_dict(xmind_file)
    # print(content)
    # 提取第一个工作簿
    workbook = content[0]['topic']
    # 初始化数据列表
    data = []

    # 定义递归函数，生成每一行数据
    def collect_titles(topic, row_data):
        row_data.append(topic['title'])

        # 如果有优先级，将其添加到 row_data 中
        if 'makers' in topic:
            row_data.extend(topic['makers'])

        # 如果有子节点，则继续递归
        if 'topics' in topic:
            for subtopic in topic['topics']:
                collect_titles(subtopic, row_data.copy())
        else:
            # 到达叶子节点，保存当前路径
            data.append(row_data)
    # 遍历根节点的所有子节点
    collect_titles(topic= workbook, row_data = [])

    # 定义优先级映射
    priority_mapping = {
        'priority-1': '高',
        'priority-2': '中',
        'priority-3': '低'
    }
    new_data = [] #  新列表用于存储处理后的数据
    count = 0 #统计不合规的测试用例
    for row in data:
        count += 1
        if len(row) < 6:
            print(f"第{count}条测试用例格式有误")
        # 检查 priority 关键字
        if not any('priority' in item for item in row):
            row.insert(-3, '')  # 在倒数第4位插入空字符串
        """
        分组列：
        数据：中心主题+最大列数-5
        格式：分组用/，例如：中心主题/分组一/分组二
        """
        # 合并倒数5个字段前的内容
        merged = '/'.join(row[:-5])  # 合并前面的部分
        row = [merged] + row[-5:]  # 用合并的字符串替换
        # 转换优先级
        for i in range(len(row)):
            if row[i] in priority_mapping:
                row[i] = priority_mapping[row[i]]
        new_data.append(row) # 添加数据到新的列表
    # 获取行数据长度，打印出测试用例的条数
    data_length = len(data)
    print(f"测试用例 {data_length} 条")

    # new_data的列名称
    columns = ['分组', '用例名称','等级','前置条件', '步骤描述', '预期结果']

    # 创建 DataFrame，转换为 Excel 格式
    df = pd.DataFrame(new_data, columns=columns)
    df['需求'] = ''  # 如果“需求”列来自“需求版本”
    df['实际结果'] = ''  # 如果“需求”列来自“需求版本”
    # 设置新的列顺序
    new_columns = ['用例名称', '分组', '等级', '需求', '前置条件', '步骤描述', '预期结果', '实际结果']
    df = df[new_columns]
    # 这里调用 convert_to_excel 函数，并传递 DataFrame 和文件名
    convert_to_excel(df, file_name + '.xlsx')
    return df


def convert_to_excel(df, file_name):
    """导出Excel方法"""
    # 创建一个 ExcelWriter 对象
    with pd.ExcelWriter(file_name, engine='openpyxl') as writer:
        # 写入第一行的文本
        first_row = pd.DataFrame({'测试用例': ['测试用例']})
        first_row.to_excel(writer, startrow=0, startcol=0, index=False, header=False)
        # 从第2行开始写入数据
        df.to_excel(writer, startrow=1, startcol=0, index=False, header=True)

    print(f"XMind文件已成功转换为Excel文件：{file_name}")

    # 设置单元格格式
    wb = load_workbook(file_name)
    ws = wb.active

    # 定义下拉选项列表
    dropdown_list = "P1,P2,P3,P4"  # 需求等级
    result = "OK,NG,Delete"  # 实际结果

    # 定义数据验证规则：需求等级
    dv = DataValidation(type="list", formula1=f'"{dropdown_list}"', showDropDown=True)
    # 定义数据验证规则：实际结果
    dv2 = DataValidation(type="list", formula1=f'"{result}"', showDropDown=True)

    # 自动检测数据的行数
    max_row = 3  # 初始化为3，因为我们将跳过前2行
    for row in ws.iter_rows(min_row=3, min_col=1, max_col=1):
        if any(cell.value is not None for cell in row):
            max_row += 1
        else:
            break  # 如果一行都是空的，则停止

    # “等级”列是第 C 列
    for row in range(3, max_row):
        cell = f'C{row}'  # C 列是“等级”列
        dv.add(ws[cell])  # 应用数据验证到每个单元格

    # “实际结果”列是第 H 列
    for row in range(3, max_row):
        cell = f'H{row}'  # H 列是“预期结果”列
        dv2.add(ws[cell])

    # 将数据验证规则应用到指定范围
    ws.add_data_validation(dv)
    ws.add_data_validation(dv2)

    # 保存更改
    wb.save(file_name)


if __name__ == '__main__':
    # 初始化 tkinter
    root = tk.Tk()
    root.withdraw()  # 隐藏主窗口
    # 弹出文件选择框
    file_path = filedialog.askopenfilename(
        title="选择xmind文件",
        filetypes=(("Text files", "*.xmind"), ("All files", "*.*"))
    )
    # 检查用户是否选择了文件
    if file_path:
        # 获取文件名和扩展名
        file_name, file_ext = os.path.splitext(os.path.basename(file_path))

        # 校验传入的文件类型是否为xmind格式
        if file_ext == '.xmind':
            print('已选择文件:', file_name)
            # 调用 parse_xmind_to_rows 函数，并传递文件路径和文件名
            parse_xmind_to_rows(file_path, file_name)
        else:
            print("请重新选择；仅支持.xmind格式的文件")
    else:
        print("您没有选择文件")